/*
###########################################################################
# File..................: RoleGeneratorController
# Version...............: 1
# Created by............: Abhishek Tandon
# Created Date..........: 15-Nov-2011
# Last Modified by......: 
# Last Modified Date....: 
# Description...........: This Controller creates the role hierarchy based on the csv fie upoaded
#               
# Change Log:               
#
# Copyright (c) 2000-2011. Astadia, Inc. All Rights Reserved.
#
# Created by the Astadia, Inc. Modification must retain the above copyright notice.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any commercial purpose, without fee, and without a written
# agreement from Astadia, Inc., is hereby forbidden. Any modification to source
# code, must include this paragraph and copyright.
#
# Permission is not granted to anyone to use this software for commercial uses.
#
# Contact address: 2839 Paces Ferry Road, Suite 350, Atlanta, GA 30339
# Company URL : http://www.astadia.com
###########################################################################
*/

public with sharing class RoleGeneratorController {
	
	// to hold the value of Access type for cases for roles
  	public String caseAccessType { get; set; }

    // to hold the value of Access type for Opportunity for roles
    public String opportunityAccessType { get; set; }

	/**
	* <P> This method is used to download the CSV file. by redirecting to the donlaod page having content type s CSV</P>
	*
	*/
    public PageReference downLoadRolesCSV() {
        return Page.RoleGeneratorDownload;
    }

    public String fileContent{ get; set; }
    public String fileContentHtml { get; set; }   
        
    //To Hold the values of files   
    private Map<Integer,List<String>>  fileContentMap=null;

    //To hold the content of Uplodaed file in blob format
    public Blob contentFile{get;set;}
    
    //To hold the content of Uplodaed file in String format
    public   String fileContentInString {get;set;}
    
    //To hold the Lines of the File, should be seprated by new line character '/n'
    private String[] filelines = new String[]{};
     
    //To hold the List of all accounts
    public List<UserRole> allRoles {get;set;}

    // To hold the name of file
    public String nameFile { get; set; }
    
    // default column value to be set for all blank column
    private final String DEFAULT_COLUMN_VALUE='$%#';
    
    // Map of User role having name as key and Id as Value
    private Map<String,Id> roleMap=null;
    
    private final String EMPTY_STRING = '';
    
    public List<String> roleHierarchy { get; set; }
    

   
    
    /**
    * </P> Method to fetch the List of Roles exist in the Orgnaisation.</p>
    */
    private List<UserRole> getAllRoles(){
        String query= 'Select u.SystemModstamp,u.RollupDescription,u.Name,u.ParentRoleId,u.Id From UserRole u';
        //return DbUtils.fireQuery(query);
        return database.query(query);
    }
        /**
        * <P> 
        * This method is used to populate the Map of user roles where role name is the key and 
        * Id is the Value for all existing role in System
        * </P>
        */
    public void populateExistingRoleMap(){
        allRoles= getAllRoles();
        roleMap= new Map<String,Id>();
        for (UserRole role : allRoles){
                if(!roleMap.containsKey(role.name)){
                roleMap.put(role.name,role.Id);
                System.debug('KEY:= '+ role.Name + ' VALUE:= '+role.Id);
                }
        }
        
        
    }
    
      /**
    <P> This method will read the file and upload the file content in the data base</P>
    */
    public Pagereference ReadFile()
    {
      try{
	        //validating the file upload and file name
	        if(!validate()){
	         System.debug(' 333333333=');
	                return null;
	        }
	
	      	convertFileContentInArray();
	      
	    	populateRoleMapping();
	      
	       if(!validateFileContent()){
	        return null;
	       }
	      
	    
	      
	      populateExistingRoleMap();
	      
	      List<RoleObject> rolesList= prepareRoleRelationships();
	      
	      if(null!= rolesList && !rolesList.isEmpty()){    
	      prepareRoleHierarchyList(rolesList); 
	      }
	      
	      //reload the Roles for display
	      allRoles=getAllRoles();
       }catch( Exception ex){
       	addErrorMessage(ex.getMessage());
       }
        return null;
        
    }
    
  
    /**
    * <P> This method is used to create a List of records uploaded in the File.</p>
    *
    *
    */   
    private void  convertFileContentInArray(){
    
        System.debug('Started');
        if(null!=contentFile){
                fileContentInString=contentFile.toString();
                        
                if(null!=fileContentInString){
                        filelines = fileContentInString.split('\n');
                }
                                
        }else{
                 addErrorMessage('Blank file is added in the CSV file');
        }
        if(null!=filelines){
                System.debug('Number of lines in the file: ' + filelines.size());
        }
    }   
    
    /**
    * <P> This method is used to create the Map of filecontent, the key of the map will be the Row number of file
    * ,and the value of map will be the List of column values</P>
    * <code> if any column is blank in the file than it will be replaced by a static string '$%#'</code>
    */
    private void populateRoleMapping(){
        if(null!=filelines){
                fileContentMap= new Map<Integer,List<String>>();
                for (Integer itr=0; itr< filelines.size();itr++){
                        
                        String [] columnValues=filelines[itr].split(',');
                        List<String> columns= new List<String>();
                        for(String columnValue : columnValues){
                                System.debug('VALUE:= ' + columnValue);
                                columns.add((StringUtils.isEmpty(columnValue)?DEFAULT_COLUMN_VALUE:columnValue));
                        }
                        
                        fileContentMap.put(itr,columns);
                }
                
                System.debug(fileContentMap);
                
                
        }
    }
    /**
    * <p>  This method is used to Prepare a Map of [parent,child] for every row of array of records of file uploaded and add that map into the List</P>
    */
   
    private List<RoleObject> prepareRoleRelationships(){
        List<RoleObject> roles= new List<RoleObject>();
        if(null!= fileContentMap){

                for(Integer itr=0; itr<fileContentMap.size(); itr++){
                        
                        List<String> columns=fileContentMap.get(itr);
                        
                        String childColumnValue=EMPTY_STRING;
                        Integer columnIndex=0;
                        
                        for(Integer itr1=0; itr1<columns.size(); itr1++){
                                if(!DEFAULT_COLUMN_VALUE.equals(columns[itr1])){
                                        childColumnValue=columns[itr1];
                                        columnIndex=itr1;
                                        break;
                                }
                        }
                        System.debug('##### columnValue:= #'+ childColumnValue + '@ columnIndex:= '+ columnIndex);
                        String parentColumnValue=EMPTY_STRING;
                        Integer currentIndex=columnIndex-1;
                        for (Integer reverseIdx=itr-1 ; reverseIdx>=0; reverseIdx-- ){
                                
                        
                                
                                List<String> columnsOfParentRow=fileContentMap.get(reverseIdx);
                                //System.debug('@@@@@ index:= ' + reverseIdx + ' LIST: = '+columnsOfParentRow);
                                
                                if(currentIndex>=0 && DEFAULT_COLUMN_VALUE != columnsOfParentRow.get(currentIndex)){
                                        parentColumnValue= columnsOfParentRow.get(currentIndex);
                                        break;
                                }
                                
                        }
                        System.debug('##### ParentcolumnValue:= '+ parentColumnValue + ' ParentcolumnIndex:= '+ currentIndex);
                        RoleObject roleObj= new RoleObject();
                        roleObj.setChildRoleName(childColumnValue);
                        roleObj.setParentRoleName(parentColumnValue);
                        System.debug('################################ child name:= '+ childColumnValue + ', := '+roleMap.get(childColumnValue));
                        roleObj.setChildRoleId(roleMap.get(childColumnValue));
                        roleObj.setParentRoleId(roleMap.get(parentColumnValue));
                        roles.add(roleObj);
                }
                
        }
        return roles;
    }
    
    /**
    * <P> This method is used for create the Roles basedon the mapping if that role doesnot exist in the Salesforce </P>
    * @param : roleMapList instance of List<RoleObject>
    *
    */
    private void prepareRoleHierarchyList(List<RoleObject> roleMapList){
        
        if(null!= roleMapList){
                
                for(RoleObject roleObj: roleMapList){

                        system.debug( roleObj.toString());
                        createRoleAndUpdateRoleMapForNewRole(roleObj);
                
                }
        }
                
    }
    

    
    /**
    * <P> This method is used to create Userrole in Salesforce based on the information provided in RoleObject</P>
    * @param  RoleObject
    */
    private void createRoleAndUpdateRoleMapForNewRole(RoleObject roleObj){
        
        UserRole userRole= null;
        
        //Child does not exist in DB
        if(null == roleObj.getChildRoleId() || StringUtils.isEmpty(roleObj.getChildRoleId())){
                
                
                
                userRole = new UserRole(name=roleObj.getChildRoleName(),RollupDescription=roleObj.getChildRoleName(),ParentRoleId=roleObj.getParentRoleId(),OpportunityAccessForAccountOwner= opportunityAccessType,CaseAccessForAccountOwner=caseAccessType);
                                
                if(null== userRole.ParentRoleId || StringUtils.isEmpty(userRole.ParentRoleId)){
                        userRole.ParentRoleId=roleMap.get(roleObj.getParentRoleName());
                }
                
                
                if(null !=userRole){
                        
                        Database.Saveresult result = Database.insert( userRole);
                        roleMap.put(userRole.name,result.getId());
                        
                }
        }
                                
        
    }
    
    
     /*<P> This method is used the validate the input data</P>
    @Return: Return the Boolean value (true/false) which govern the success or failure of validation
    */
    private Boolean validate(){
        Boolean isValid=true;
        
        isValid =validateFileUploaded(isValid);
         System.debug(' isValid1111111111='+ isValid);
        if(isValid){ 
        isValid= validateFileExtension(nameFile);
        }
        System.debug(' isValid=2222222'+ isValid);
        
        return isValid;
    }
    
    /**
    <P> This method is used to validate whether the file is uploaded or not. </p>
    */
    
    private Boolean validateFileUploaded(Boolean isValid){
                if(null==nameFile ){
                        System.debug('Isvalid:='+ isValid);
                addErrorMessage('Please select the file to upload First');
                isValid=false;
            
          
        }
       
        return isValid;
    }
    
      /**
    <P> This method is used to validate whether the file extension is CSV or not. </p>
    */
     private Boolean  validateFileExtension(String fileName){
                        Boolean isValid=true;   
                        final String EXTENTION ='csv';
                if(null==fileName || StringUtils.isEmpty(fileName) || !fileName.toLowerCase().endsWith(EXTENTION)){
            
                                isValid= false;
                        addErrorMessage('Invaid format file is uploaded, please upload correct CSV file');
                }
               return isValid;
    }   
    
    /**
    * <P> This method is used to validate the content of file on a high level, it validate following things</P>
    * <UL>
        <LI> No blank line should be in the file</LI>
        <LI> Each line should have exactly one content</LI> 
    * </UL>
    */
    private Boolean validateFileContent(){
        Boolean isValid=true;
        
        
        List<Integer> blankLines= new List<Integer>();
        List<Integer> multipleValues= new List<Integer>();
        
        if(null!= fileContentMap && !fileContentMap.isEmpty()){
        Set<Integer> keys= fileContentMap.keySet();
        
                for( Integer key: keys ){
                        Boolean valueFound=false;
                        List<String> values=fileContentMap.get(key);
                        
                
                        for(String value :  values){
                                
                                if(!DEFAULT_COLUMN_VALUE.equals(value) && !StringUtils.isEmpty(value) && !valueFound){
                                        valueFound = true;
                                }else if(!DEFAULT_COLUMN_VALUE.equals(value) && !StringUtils.isEmpty(value) && ''!=value && valueFound){
                                        multipleValues.add(key+1);
                                }
                        }
                        
                        if(!valueFound){
                                blankLines.add(key+1);
                        }
                        
                }
                if(!blankLines.isEmpty()){
                        isValid=false;
                        addErrorMessage('BlankLine found in the Uploaded files:= '+ nameFile + ' at line(s):= '+blankLines);
                }
                if(!multipleValues.isEmpty()){
                        isValid=false;
                        addErrorMessage('Multiple values found in single line in the Uploaded files:= '+ nameFile );
                }
        }
                
        
        return isValid; 
    }  
       
     

 
 /**
 * <P> This method is used to extract the Role hiratchy from the Visual force Page</p>
 *
 */
 public PageReference extractRoleHierarcy(){
        prepareRoleHierarchy();
        return downLoadRolesCSV();
 }
 
 /**
 * <P> This method is used to preparing  the Role hiratchy by calling the populateRoleHirarchy method</p>
 *
 */
 private void prepareRoleHierarchy(){
        
        roleHierarchy= new List<String>();
        fileContent=EMPTY_STRING;
        fileContentHtml=EMPTY_STRING;

        UserRole masterRole= [select id,name, parentroleid from userrole where parentroleid = null order by parentroleid LIMIT 1];
        
        if(null!= masterRole ){
        	
        	
        	roleHierarchy.add(StringUtils.replace(masterRole.Name, ',', DEFAULT_COLUMN_VALUE));
        	roleHierarchy=populateRoleHirarchy(masterRole, 0,roleHierarchy);
        }
        System.debug('$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ Role: '+roleHierarchy);
        Integer numberOfRows=roleHierarchy.size();
        
        for (Integer itr=0; itr<numberOfRows;itr++ ){
        	if(itr==0){
        		fileContentHtml= '<table>';
        	}
        	Integer numberOFcol= StringUtils.countMatches(roleHierarchy.get(itr), ',') ;
        	System.debug('%%%%%%%numberOFcol:= '+numberOFcol);
        	if(itr==numberOfRows-1){
        		fileContent = fileContent + roleHierarchy.get(itr);
        		fileContentHtml= fileContentHtml + '</table>';
        		
        	}else{
        		fileContent = fileContent + roleHierarchy.get(itr) + '\n';
        		fileContentHtml= fileContentHtml + '<TR>';
        		
        		if(numberOFcol!=0){
        			for (Integer itr1=0; itr1<numberOFcol; itr1++){
        				fileContentHtml= fileContentHtml + '<td></td>' ; 
        			}
        			
        		}
        		fileContentHtml= fileContentHtml + '<td>' + roleHierarchy.get(itr) + '</td></tr>';
        	}
        	
        }
        System.debug(fileContent);
        fileContentHtml=fileContentHtml.replaceAll(',', '');
        System.debug(fileContentHtml);
        createAndSaveFile(fileContent);

 }
 
 /**
 * <P> This method is used to preparing  the Role hirarchy by calling the populateRoleHirarchy method.</p>
 *  @Param : parentRole instance of UserRole for which child List is required
 *  @Param : level instance of Integer which denotes the level of a child role in the hirarchy
 *  @Param : roleHierarchy instance of  List<String>  which contains the final element all roles
 */
        
 	private List<String> populateRoleHirarchy(UserRole parentRole,Integer level,  List<String> roleHierarchy){

        
	   	Integer childlevel=level+1;
	   	
        	
       	List<UserRole> childRoles=[select id,name, parentroleid from userrole where parentroleid =: parentRole.id order by id];
        	
       	for (UserRole childRole : childRoles){
        		//System.debug('@@@@@@@@@@@@@@@ Rolename:= '+ childRole.Name+ ' , Level:= '+childlevel);
        		
        		String prefix=',';
        		for (integer itr=1;itr<=level;itr++){
        			prefix = prefix + ',';
        		}
        		
        		//roleHierarchy.add(prefix +childRole.Name.replaceAll(',', DEFAULT_COLUMN_VALUE));
        		roleHierarchy.add(prefix + StringUtils.replace(childRole.Name, ',', DEFAULT_COLUMN_VALUE));
        		//System.debug('@@@@@@@@@@@@@@@ List:= '+ roleHierarchy);
        		populateRoleHirarchy(childRole, childlevel,roleHierarchy);
        		
        	}
        
        
        return roleHierarchy;
        
       
 	}
 
 
 	/**
 	* <P> This method is used to create and save file to the user System </p>
 	*/
 	private void createAndSaveFile(String fileContent){
 		Document document= new Document();
 		
 		
 	}
 	
 	
 	/**
	 * <P> This method is used to add Error message to the current Visualforce page, based on the Message text provided.</P>
	 * 
	 * @param: message as String that message to be added.
	 */
	private  void addErrorMessage(String message) {
		
        ApexPages.addMessage(new ApexPages.Message(ApexPages.severity.ERROR,message));
         System.debug('Added Error Message := ' + Message );
		
	}
	

 /**
 * <P> Inner class for handeling role objects. </p>
 *
 */
 	class RoleObject {
        
	        String parentRoleName;
	        String parentRoleId;
	        
	        String childRoleName;
	        String childRoleId;
         /* @return the parentRoleName
                 */
                public String getParentRoleName() {
                        return parentRoleName;
                }

                /**
                 * @param parentRoleName the parentRoleName to set
                 */
                public void setParentRoleName(String parentRoleName) {
                        this.parentRoleName = parentRoleName;
                }

                /**
                 * @return the parentRoleId
                 */
                public String getParentRoleId() {
                        return parentRoleId;
                }

                /**
                 * @param parentRoleId the parentRoleId to set
                 */
                public void setParentRoleId(String parentRoleId) {
                        this.parentRoleId = parentRoleId;
                }

                /**
                 * @return the childRoleName
                 */
                public String getChildRoleName() {
                        return childRoleName;
                }

                /**
                 * @param childRoleName the childRoleName to set
                 */
                public void setChildRoleName(String childRoleName) {
                        this.childRoleName = childRoleName;
                }

                /**
                 * @return the childRoleId
                 */
                public String getChildRoleId() {
                        return childRoleId;
                }

                /**
                 * @param childRoleId the childRoleId to set
                 */
                public void setChildRoleId(String childRoleId) {
                        this.childRoleId = childRoleId;
                }
                
                
}
 
}